package org.sanguine.study.hometasks.second;

import java.math.BigDecimal;
import java.util.NavigableSet;
import java.util.TreeSet;

/**
 * List of numbers with find closest method.  Repeatable values is not allowed.
 * @author Alexey Kuznetsov
 *
 * @param <T> type of elements
 */

public class NumbersList<T extends Number> {
	
	private NavigableSet<T> list;

	public NumbersList() {
		list = new TreeSet<>();
	}
	
	/**
	 * Add new value in list
	 * @param value
	 * @return true if state of the list was changed, false - otherwise
	 */
	public boolean add(T value) {
		return list.add(value);
	}
	
	/**
	 * Remove value from list
	 * @param value to remove
	 * @return true if state of the list was changed, false - otherwise
	 */
	
	public boolean remove(T value) {
		return list.remove(value);
	}
	
	/**
	 * Find the closest to value number. If there is two numbers with same distance from value than return less than value number 
	 * @param value
	 * @return the closest to value number (may be equals to value) or null if list is empty or value is null 
	 */
	
	public T findClosest(T value) {
		
		if (value == null || list.isEmpty()) return null;
		
		T floor = list.floor(value);
		if (value.equals(floor)) return floor;
		
		T ceiling = list.ceiling(value);
		if (value.equals(ceiling)) return ceiling;
		
		if (floor == null) return ceiling;
		else if (ceiling == null) return floor;

		return closest(value, floor, ceiling);
	}

	private T closest(T value, T floor, T ceiling) {
		BigDecimal bigValue = new BigDecimal(value.toString());
		BigDecimal bigFloor = new BigDecimal(floor.toString());
		BigDecimal bigCeiling = new BigDecimal(ceiling.toString());
		
		BigDecimal floorValueModulus = bigValue.add(bigFloor.negate());
		BigDecimal ceilingValueModulus = bigCeiling.add(bigValue.negate());
		
		if (floorValueModulus.compareTo(ceilingValueModulus) > 0) return ceiling;
		return floor;
	}

}
